// Copyright (c) [Year] [name of copyright holder]
// [Software Name] is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2. 
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2 
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
// See the Mulan PSL v2 for more details.

/*!
 * \autor captainpeng
 * \date 2018-9-5
 * \update 2019-12-17
 * \version 1.0
 * \copyright
 */

#ifndef MY_UNICODE_BUFFER_HPP
#define MY_UNICODE_BUFFER_HPP

#include"mystd.hpp"

namespace my{
    class ubuffer{
    public:
        using unicodeType = unicode_t;
        using bufType = my::buffer<unicode_t>;
        static const int BUFFER_SIZE = 4096;

    private:
        bufType mBuf;
        unicodeType * mForward;
        unicodeType * mBack;

        int mRows;
        int mCols;
        
        // inline void init(){
        //     if(!mStream.is_open()) return ;
            
        //     mBuf.resize(size);
            
        //     mRows = mCols = 1;
        //     mForward = mBack = mBuf.begin()+1;
        //     int size = mBuf.size() - 1;
        //     mStream.read(mForward, size);
        //     int count = mStream.gcount();
        //     std::cout<<std::dec<<"count = "<<count<<std::endl;
        //     count = (count == size || count == 0)? 0:count+1;
        //     mBuf[count] = unicode::eof;
            
        //     //第一次填充空间时候，我们将从第1位开始填充，为的是让
        //     //若空间都被填充满时候，第 0 位为 eof ，若不是，则文件一次性被读取完

        // }

    public:

        ubuffer():mBuf(BUFFER_SIZE), mRows(1), mCols(0)
            {
                mBack = mForward = mBuf.begin();
                (*mForward) = unicode::eof;
            }

        ubuffer(const ubuffer & buf)=delete;
        ubuffer & operator=(const ubuffer & buf) = delete;

        ubuffer(ubuffer && buf):
            mBuf(std::move(buf.mBuf)),
            mForward(buf.mForward),
            mBack(buf.mBack),
            mRows(buf.mRows),
            mCols(buf.mCols)
            {

            }

        ubuffer & operator=(ubuffer && buf){
            if(&buf == this)
                return *this;

            mBuf = std::move(buf.mBuf);
            mForward = buf.mForward;
            mBack = buf.mBack;

            mRows = buf.mRows;
            mCols = buf.mCols;

            buf.mForward = buf.mBack = buf.mBuf.begin();
            *(buf.mForward) = unicode::eof;
            return *this;
        }
        
// ------ 操作 ---------
        inline bool is_load(){
            int count = mBack - mForward;
            return (count != 1 || (-count) != (BUFFER_SIZE - 1));
        }

        bool load(ufile & file){
            //若mForward所指的数据块为eof，则进行数据重新填充
            if(*mForward == unicode::eof){
                
                if(file.eof()){
                    file.close();
                    return false;
                }
                
                unicode_t * S = mBuf.begin();
                unicode_t * E = mBuf.end();

                int count = mBack - mForward;
                //count用于计算mBack与mForward之间是否有可以填充的区间
                //assert((count != 1 || (-count) != (size-1)));

                my::assure((count != 1 || (-count) != (BUFFER_SIZE - 1)), "my::ubuffer::load, buffer 已经满了");
		    
                // 当无可填充空间时候，退出程序，无填充区间的有两种可能:
                //1. mBack 在 mForward 前面，此时可填充空间在mBack-mForward之间
                //   也就是说，当他们之间不存在任何数据块的时候，此时 count==1
                //2. mBack 在 mForward 后面，此时不可填充空间在 mBack-mForward 之间
                //   也就是说，当 mForward 在 E-1，mBack在B的时候,他们之间才不存在
                //   可填充空间，此时 -count==size-1
		    
                if(mForward < mBack){
                    count = mForward - S;
                    file.read(mForward, (mBack - mForward - 2));
                    count += file.gcount();
                    //这时候利用 count 计算的是 mBuf 中的位置所以
                    //count=mForward偏移+进入buf的数据量
                }else{
                    //这种情况的填充将是分两段进行
                    // 1. 是 mForward 到 E 的情况
                    // 2. 是 S 到 mBack-1 的情况
			    
                    count = E - mForward;
                    file.read(mForward, ((mBack == S)? count-1:count));
                    count = file.gcount() - count;
                    
                    //如果count==0,证明情况1被填充完，需要填充情况2
                    //需要注意的是，当 mBack 刚好在 B 上，情况2可填充的空间则为0,
                    //所以我们需要在填充情况1的时候，把这个条件带来的失误给消灭
                    if(count == 0){
                        file.read(S, (mBack - S - 2));
                        count = file.gcount();
                    }
                    
                    //这时候利用 count 计算的是 mBuf 中的位置，
                    //但是需要注意的是 mBuf 可以满足负数
                    //所以我们可以放任 count 为负数
                    
                }
                
                mBuf[count] = unicode::eof;
            }

            return true;
        }

        unicodeType nextUnicode(ufile & file){
            unicodeType c = *mForward;
            if(c == unicode::eof && is_load()){
                load(file);
                c = *mForward;
            }

            if(c != unicode::eof){
                //把mForward向前移动
                mForward = (mForward == mBuf.end() - 1)? mBuf.begin():mForward+1;
            }

            return c;
        }

        unicodeType throwUnicode(){
            unicodeType u = unicode::empty;

            if(mBack != mForward){
                u = *mBack;
                if(u == static_cast<unicodeType>('\n')){
                    ++mCols = 0;
                    ++mRows;
                }else ++mCols;
		
                mBack = (mBack == mBuf.end()-1)? mBuf.begin():mBack+1;
            }

            return u;
        }

        void throwOnly(){
            while(throwUnicode() != unicode::empty);
        }

        bool backUnicode(){
            if(mForward != mBack){
                //回溯要求 mForward 不能超越 mBack
                mForward = (mForward == mBuf.begin())? mBuf.end() - 1:mForward - 1;
                return true;
            }

            return false;
        }

        ustring throwUString(){
            int size = (mForward > mBack)? mForward - mBack : (mBuf.end() - mBack) + (mForward - mBuf.begin());
            unicodeType * buf = new unicodeType[size+1];
            int top = -1;
            unicodeType u;
            while((u = throwUnicode()) != unicode::empty)
                buf[++top] = u;
            buf[++top] = unicode::empty;
            ustring s(buf);
            delete buf;
            return s;
        }

        inline int cols(){
            return mCols;
        }

        inline int rows(){
            return mRows;
        }

    };
}

#endif
